module nazo.common_type;
import std.typetuple, std.traits, std.stdio;

/**
 * 共通の型の内一番上の型を返します。
 */
template CommonType(TL...){
  alias AllCommonType!(TL)[0] CommonType;
}

/**
 * 共通の型を上順に全て返します。
 */
template AllCommonType(TL...){
  static if(TL.length==0)
    static assert(0, "This template need one or more arguments.");
  else static if(TL.length==1)
    alias TL AllCommonType;
  else
    alias CommonTuple!(tmp5!(AllTypeTuple, TL)) AllCommonType;
}

template tmp5(alias temp, TL...){
  static if(TL.length==0)
    static assert(0);
  else static if(TL.length==1)
    alias temp!(TL[0]) tmp5;
  else
    alias TypeTuple!(temp!(TL[0]), void, tmp5!(temp, TL[1..$])) tmp5;
}

/**
 * 重複を取り除いた全ての派生元+それ自身の型タプルを返します。
 */
template AllTypeTuple(T){
  alias TypeTuple!(AllBaseTypeTuple!(T), T) AllTypeTuple;
}

/**
 * 重複を取り除いた全ての派生元の型タプルを返します。
 */
template AllBaseTypeTuple(T){
  alias NoDuplicates!(tmp!(false, BaseTypeTuple!(T))) AllBaseTypeTuple;
}

template tmp(bool has_object){
  static if(has_object)
    alias TypeTuple!(Object) tmp;
  else
    alias TypeTuple!() tmp;
}

template tmp(bool has_object, T, TL...){
  static if(BaseTypeTuple!(T).length>0)
    alias TypeTuple!(T, AllBaseTypeTuple!(T), tmp!(has_object, TL)) tmp;
  else static if(is(T == Object))
    alias TypeTuple!(tmp!(true, TL)) tmp;
  else
    alias TypeTuple!(T, tmp!(has_object, TL)) tmp;
}


class A:B,D{};
class B:C,D{};
class C:D{};
interface D{};

static assert(is(AllBaseTypeTuple!(A) == TypeTuple!(B, C, D, Object)));

/**
 * 二つ以上のタプルの共通部分を返します。
 * Example: CommonTuple(tuple1, void, tuple2, void, tuple3);
 */
template CommonTuple(TL...){
  static if(is(After!(void, TL)==void))
    alias TL CommmonTuple;
  else static if(is(After!(void, After!(void, TL))==void))
    alias tmp2!(Before!(void, TL), void, After!(void, TL)) CommonTuple;
  else
    alias CommonTuple!(tmp2!(Before!(void, TL), void, Before!(void, After!(void, TL))), void, After!(void, After!(void, TL))) CommonTuple;
}

static assert(is(CommonTuple!(long, int, void, long)
                ==TypeTuple!(long)),CommonTuple!(long, int, void, long));

template tmp2(TL...){
  static if(is(After!(void, TL)==void))
    alias TypeTuple!() tmp2;
  else static if(IndexOf!(TL[0], After!(void, TL)) == -1)
    alias TypeTuple!(tmp2!(TL[1..$])) tmp2;
  else
    alias TypeTuple!(TL[0], tmp2!(TL[1..$])) tmp2;
}

/**
 * ULの中からTを見つけ、それ以降を返します。
 * もし見付からなかったらvoidを返します。
 */
template After(T, UL...){
  static if(UL.length == 0)
    alias void After;
  else static if(is(T==UL[0]))
    alias UL[1..$] After;
  else
    alias After!(T, UL[1..$]) After;
}

static assert(is(After!(long, TypeTuple!(int, uint, long, ulong))
                ==TypeTuple!(ulong)));

/**
 * ULの中からTを見つけ、それ以前を返します。
 * もし見付からなかったらvoidを返します。
 */
template Before(T, UL...){
  alias tmp4!(T, 0, UL) Before;
}

template tmp4(T, int i, UL...){
  static if(UL.length == i)
    alias void tmp4;
  else static if(is(T==UL[i]))
    alias UL[0..i] tmp4;
  else
    alias tmp4!(T, i+1, UL) tmp4;
}

static assert(is(Before!(uint, TypeTuple!(int, uint, long, ulong))
                ==TypeTuple!(int)));

/*
interface E{}
class A:B,E{}
class A2:B,E{}
class B:C,E{}
class C:D,E{}
class D{}

void main(){
  writefln(typeid(AllBaseTypeTuple!(A)));
  writefln(typeid(CommonType!(A, B, D)));
  writefln(typeid(CommonType!(A, A2)));
}
*/
